import tkinter as tk
import constants as const
from eventcenter import eventcenter as event
from datacenter import datacenter as dc
from tools import tools


class RightPanel(tk.Frame):
    """右侧面板类，用于显示和编辑卡片属性
    
    该类继承自tk.Frame，负责显示当前选中卡片的详细信息，并提供编辑功能。
    包括卡片的花色、数字、层级、位置等属性的显示和修改。
    """
    
    def __init__(self, root):
        """初始化右侧面板
        
        Args:
            root: 父级窗口组件
        """
        super().__init__(root)
        # self.pack(side=tk.LEFT, fill=tk.BOTH, padx=5)
        self.grid(row=0, column=2, sticky="ns")
        self.root = root

        self.value_options = ["自定义", "规则定义"]
        self.card_pool_options = [0, 101, 102, 103]
        

        self.value_def = [[], [], []]

        self.uid = const.NONE_INT
        
        self._init_event()
        
        self.data = None
        self.chb_var = tk.BooleanVar()
        # 创建容器 Frame
        self._create_widgets()

    def _init_event(self):
        """初始化事件监听
        
        注册卡片选择、卡片移动和菜单点击的事件处理函数
        """
        event.add_event(const.event_select_card, self._event_select_card)
        event.add_event(const.event_select_card_move, self._event_select_card_move)
        event.add_event(const.event_guide_menu_cliciked, self._event_guide_menu_clicked)

    def _create_widgets(self):
        """创建界面组件
        
        创建并布局所有UI组件，包括标签、按钮、选项菜单等
        """
        tk.Button(self, text="更新", command=self.on_save_click).pack(side=tk.TOP, fill=tk.X, pady=12, padx=5)

        self.id_label = tk.Label(self, text="none-none", anchor=tk.W)
        self.id_label.pack(side=tk.TOP, fill=tk.X, ipady=3)
        self.face_checkbox = tk.Checkbutton(self, text=" 初始正面", anchor=tk.W, variable=self.chb_var)
        self.face_checkbox.pack(side=tk.TOP, fill=tk.X, ipady=2)
        # 添加复选框值变化的事件处理
        self.chb_var.trace_add("write", self.on_face_change)

        # 控制旋转角度的滑动条
        rotation_frame = tk.Frame(self)
        rotation_frame.pack(side=tk.TOP, fill=tk.X, ipady=5)
        tk.Label(rotation_frame, text="旋转").pack(side=tk.LEFT, fill=tk.X)
        self.rotation_var = tk.IntVar()
        self.rotation_var.set(0)
        self.rotation_scale = tk.Scale(rotation_frame, from_=0, to=360, orient=tk.HORIZONTAL, 
                                     variable=self.rotation_var, command=self.on_rotation_change)
        self.rotation_scale.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)

        # 添加一个输入空框，表示旋转的角度，请注意和rotation_var关联，当输入改变时滑动条变化，滑动条变化时输入框也变化
        self.rotation_entry_var = tk.StringVar()
        self.rotation_entry_var.set("0")
        self.rotation_entry = tk.Entry(rotation_frame, textvariable=self.rotation_entry_var, width=5)
        self.rotation_entry.pack(side=tk.LEFT, padx=5)
        
        # 添加输入框值变化的事件处理
        self.rotation_entry_var.trace_add("write", self.on_rotation_entry_change)

        f1 = tk.Frame(self)
        f1.pack(side=tk.TOP, fill=tk.X, ipady=5)
        tk.Label(f1, text="牌面值定义：").pack(side=tk.LEFT, fill=tk.X)

        self.value_option = tk.StringVar(self)
        self.value_option.set(self.value_options[0])
        self.value_option.trace_add("write", self.on_change_generate_type)
        tk.OptionMenu(f1, self.value_option, *self.value_options).pack(side=tk.LEFT, fill=tk.X)

        # 自定义牌面
        f2 = tk.Frame(self)
        f2.pack(side=tk.TOP, fill=tk.X, ipady=5)
        tk.Label(f2, text="花色：").pack(side=tk.LEFT, fill=tk.X)

        self.suite_option = tk.StringVar(self)
        self.suite_option.set(const.suits_str[0])
        tk.OptionMenu(f2, self.suite_option, *const.suits_str).pack(side=tk.LEFT, fill=tk.X, pady=5)
        self.suite_option.trace_add("write", self.on_change_rank_or_suit)
        

        f3 = tk.Frame(self)
        f3.pack(side=tk.TOP, fill=tk.X, ipady=5)
        tk.Label(f3, text="数字：").pack(side=tk.LEFT, fill=tk.X)
        self.num_option = tk.StringVar(self)
        self.num_option.set(const.ranks_str[0])
        tk.OptionMenu(f3, self.num_option, *const.ranks_str).pack(side=tk.LEFT, fill=tk.X, pady=5)
        self.num_option.trace_add("write", self.on_change_rank_or_suit)

        self.value_def[0].append(f2)
        self.value_def[0].append(f3)

        # 规则定义
        f4 = tk.Frame(self)
        tk.Label(f4, text="牌库：").pack(side=tk.LEFT, fill=tk.X)

        self.card_pool_option = tk.StringVar(self)
        self.card_pool_option.set(self.card_pool_options[0])
        tk.OptionMenu(f4, self.card_pool_option, *self.card_pool_options).pack(side=tk.LEFT, fill=tk.X, pady=5)

        f5 = tk.Frame(self)
        tk.Label(f5, text="规则库：").pack(side=tk.LEFT, fill=tk.X)

        self.card_rule_option = tk.StringVar(self)
        
        card_rule_options = dc.get_rule_keys()
        self.card_rule_option.set(card_rule_options[0])
        tk.OptionMenu(f5, self.card_rule_option, *card_rule_options).pack(side=tk.LEFT, fill=tk.X, pady=5)

        self.card_rule_option.trace_add("write", self.on_change_rule)

        self.value_def[1].append(f4)
        self.value_def[1].append(f5)

        f6 = tk.Frame(self)
        f6.pack(side=tk.TOP, fill=tk.X)
        self.layer_id_label = tk.Label(f6, text=f"层级：none", anchor=tk.W)
        self.layer_id_label.pack(side=tk.LEFT, fill=tk.X)
        tk.Button(f6, text="⬆", command=self.on_up_layer).pack(side=tk.LEFT, fill=tk.X, padx=2)
        tk.Button(f6, text="⬇", command=self.on_down_layer).pack(side=tk.LEFT, fill=tk.X, padx=2)
        
        f7 = tk.Frame(self)
        f7.pack(side=tk.TOP, fill=tk.X, ipady=10)
        self.pos_x_label = tk.Label(f7, text="X:", padx=1)
        self.pos_x_label.pack(side=tk.LEFT, fill=tk.X)

        # 添加一个输入空框显示x坐标的值默认0.0紧跟pos_x_label后面，并添加输入事件监听on_pos_x_change
        self.pos_x_entry_var = tk.StringVar(self)
        self.pos_x_entry_var.set("0.0")
        self.pos_x_entry = tk.Entry(f7, textvariable=self.pos_x_entry_var, relief="solid", width=5, borderwidth=1)
        self.pos_x_entry.pack(side=tk.LEFT, fill=tk.X)
        # 添加输入框值变化的事件处理
        self.pos_x_entry_var.trace_add("write", self.on_pos_x_change)

        self.pos_y_label = tk.Label(f7, text="Y:", padx=3)
        self.pos_y_label.pack(side=tk.LEFT, fill=tk.X, padx=1)

        # 添加一个输入空框显示y坐标的值默认0.0紧跟pos_y_label后面
        self.pos_y_entry_var = tk.StringVar(self)
        self.pos_y_entry_var.set("0.0")
        self.pos_y_entry = tk.Entry(f7, textvariable=self.pos_y_entry_var, relief="solid", width=5, borderwidth=1)
        self.pos_y_entry.pack(side=tk.LEFT, fill=tk.X)
        # 添加输入框值变化的事件处理
        self.pos_y_entry_var.trace_add("write", self.on_pos_y_change)

        f8 = tk.Frame(self)
        f8.pack(side=tk.TOP, fill=tk.X, ipady=5)
        self.cur_id_label = tk.Label(f8, text="id: none")
        self.cur_id_label.pack(side=tk.RIGHT, fill=tk.X)

        self.value_def[2].append(f6)
        self.value_def[2].append(f7)
        self.value_def[2].append(f8)

    def on_change_rule(self, var_name, index, mode):
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")

        data.ruleid = self.card_rule_option.get()
        data.cardpoolid = self.card_pool_option.get()

        option_str = self.value_option.get()
        index = self.value_options.index(option_str)
        data.iscustom = False if index == 0 else True

        event.dispatch_event(const.event_card_value_changed, data.uid)

    def on_change_rank_or_suit(self, var_name, index, mode):
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")

        suit_s = self.suite_option.get()
        rank_s = self.num_option.get()
        cardid = data.to_id(suit_s, rank_s)
        data.cardid = cardid

        option_str = self.value_option.get()
        index = self.value_options.index(option_str)
        data.iscustom = False if index == 0 else True

        event.dispatch_event(const.event_card_value_changed, data.uid)
    def on_rotation_change(self, value):
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")
        
        # 转换为整数并限制范围
        value = int(value)
        if value < 0: value = 0
        if value > 360: value = 360
        
        # 更新卡片的rotation属性
        data.rotation = value
        
        # 更新输入框的值
        self.rotation_entry_var.set(value)
        
        # 触发卡片值变更事件，通知其他组件更新显示
        event.dispatch_event(const.event_card_value_changed, data.uid)
        
    def on_rotation_entry_change(self, *args):
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")

        try:
            # 获取输入框的值
            value = self.rotation_entry_var.get()
            if value == "": return
            
            # 转换为整数并限制范围
            value = int(value)
            if value < 0: 
                value = 0
                self.rotation_entry_var.set("0")
            if value > 360:
                value = 360
                self.rotation_entry_var.set("360")
            
            # 更新滑动条的值
            self.rotation_var.set(value)
            
            # 更新卡片的rotation属性
            data = self.data
            if data is None: return
            data.rotation = value
            
            # 触发卡片值变更事件，通知其他组件更新显示
            event.dispatch_event(const.event_card_value_changed, data.uid)
        except ValueError:
            # 输入的不是有效的数字，恢复为当前滑动条的值
            if self.data:
                self.rotation_entry_var.set(str(self.data.rotation))

    def on_save_click(self):
        """保存按钮点击事件处理函数
        
        保存当前编辑的卡片数据
        """
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")

        suit_s = self.suite_option.get()
        rank_s = self.num_option.get()
        cardid = data.to_id(suit_s, rank_s)

        data.firstopen = self.chb_var.get()
        data.cardid = cardid

        option_str = self.value_option.get()
        index = self.value_options.index(option_str)
        data.iscustom = False if index == 0 else True

        data.ruleid = self.card_rule_option.get()
        data.cardpoolid = self.card_pool_option.get()

        dc.set_card(data)

        tools.toast("更新成功")
        event.dispatch_event(const.event_card_value_changed, data.uid)

    def on_change_generate_type(self, *args):
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")

        option_str = self.value_option.get()
        index = self.value_options.index(option_str)

        value_def_len = len(self.value_def)
        for i in range(value_def_len):
            vs = self.value_def[i]
            for _, v in enumerate(vs):
                if i == index:
                    v.pack(side=tk.TOP, fill=tk.X, ipady=5)
                else:
                    v.pack_forget()

        [v.pack(side=tk.TOP, fill=tk.X, ipady=5) for v in self.value_def[2]]

    def _refresh_layer(self, uid):
        """刷新层级信息
        
        根据卡片UID更新界面显示的卡片信息
        
        Args:
            uid: 卡片的唯一标识符
        """
        data = dc.get_card(uid)
        self.data = data
        if data is None: return

        self.id_label.config(text=f"编号：{data.layerid}-{data.index}")
        self.cur_id_label.config(text=f"id: {data.uid}")
        self.layer_id_label.config(text=f"层级：{data.layerid}")

        self.chb_var.set(data.firstopen)
        self.value_option.set(self.value_options[data.iscustom])

        self.rotation_var.set(data.rotation)
        self.rotation_entry_var.set(str(data.rotation))

        if data.iscustom:
            self.card_pool_option.set(data.cardpoolid)
            self.card_rule_option.set(data.ruleid)
        else:  
            suit = data.suit(True)
            rank = data.rank(True)
            self.suite_option.set(suit)
            self.num_option.set(rank)

        self._update_pos(data.x, data.y)

        

    def _update_pos(self, dx, dy):
        self._updating = True  # 设置标志
        x, y = tools.convert_to_centered(dx, dy)
        self.pos_x_entry_var.set(str(x))
        self.pos_y_entry_var.set(str(y))
        self._updating = False  # 清除标志

    def _event_guide_menu_clicked(self, uid, layerid, index):
        """菜单点击事件处理函数
        
        处理左侧菜单导航点击事件
        
        Args:
            uid: 卡片的唯一标识符
            layerid: 层级ID
            index: 卡片索引
        """
        self._refresh_layer(uid)

    def _event_select_card(self, uid, selected):
        """卡片选择事件处理函数
        
        处理卡片选择事件
        
        Args:
            uid: 卡片的唯一标识符
            selected: 是否选中
        """
        self._refresh_layer(uid)

    def _event_select_card_move(self, cardid, dx, dy):
        """卡片移动事件处理函数
        
        处理卡片移动事件，更新位置信息
        
        Args:
            cardid: 卡片ID
            dx: X坐标
            dy: Y坐标
        """
        self._update_pos(dx, dy)

    def on_up_layer(self):
        """上移层级按钮点击事件处理函数
        
        将当前卡片上移一层
        """
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")

        if data.layerid >= len(dc.curmap.cuids) - 1: return tools.toast("已经是最顶层了")

        dc.curmap.adjust_layer_index(data.uid, data.layerid, 1)
        self._refresh_layer(data.uid)

        event.dispatch_event(const.event_card_value_changed, data.uid, True)
    
    def on_down_layer(self):
        """下移层级按钮点击事件处理函数
        
        将当前卡片下移一层
        """
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")

        if data.layerid == 0: return tools.toast("已经是最底层了")

        dc.curmap.adjust_layer_index(data.uid, data.layerid, -1)
        self._refresh_layer(data.uid)
        event.dispatch_event(const.event_card_value_changed, data.uid, True)
        
    def on_pos_x_change(self, *args):
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")

        if hasattr(self, '_updating') and self._updating:
            return
        try:
            # 获取输入框的值
            x_value = self.pos_x_entry_var.get()
            y_value = self.pos_y_entry_var.get()
            if x_value == "" or y_value == "": return
            
            # 转换为浮点数
            x = float(x_value)
            y = float(y_value)
            
            # 将中心坐标系的值转换为左上角坐标系的值
            left_x, left_y = tools.convert_to_left(x, y)
            
            # 限制坐标在游戏绘制区域内
            if left_x < const.fix_distance:
                left_x = const.fix_distance
            elif left_x > const.game_window_width - const.card_width - const.fix_distance:
                left_x = const.game_window_width - const.card_width - const.fix_distance
            
            if left_y < const.fix_distance:
                left_y = const.fix_distance
            elif left_y > const.game_window_height - const.card_height - const.fix_distance:
                left_y = const.game_window_height - const.card_height - const.fix_distance
            
            # 更新卡片的位置
            data = self.data
            if data is None: return
            data.x = left_x
            data.y = left_y
            
            # 如果坐标被限制，更新输入框显示的值
            centered_x, centered_y = tools.convert_to_centered(left_x, left_y)
            if centered_x != x or centered_y != y:
                self.pos_x_entry_var.set(str(centered_x))
                self.pos_y_entry_var.set(str(centered_y))
            
            # 触发卡片值变更事件，通知其他组件更新显示
            event.dispatch_event(const.event_card_value_changed, data.uid)
        except ValueError:
            # 输入的不是有效的数字，恢复为当前位置的值
            if self.data:
                x, y = tools.convert_to_centered(self.data.x, self.data.y)
                self.pos_x_entry_var.set(str(x))
                self.pos_y_entry_var.set(str(y))
    
    def on_pos_y_change(self, *args):
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")

        if hasattr(self, '_updating') and self._updating:
            return
        try:
            # 获取输入框的值
            x_value = self.pos_x_entry_var.get()
            y_value = self.pos_y_entry_var.get()
            if x_value == "" or y_value == "": return
            
            # 转换为浮点数
            x = float(x_value)
            y = float(y_value)
            
            # 将中心坐标系的值转换为左上角坐标系的值
            left_x, left_y = tools.convert_to_left(x, y)
            
            # 限制坐标在游戏绘制区域内
            if left_x < const.fix_distance:
                left_x = const.fix_distance
            elif left_x > const.game_window_width - const.card_width - const.fix_distance:
                left_x = const.game_window_width - const.card_width - const.fix_distance
            
            if left_y < const.fix_distance:
                left_y = const.fix_distance
            elif left_y > const.game_window_height - const.card_height - const.fix_distance:
                left_y = const.game_window_height - const.card_height - const.fix_distance
            
            # 更新卡片的位置
            data = self.data
            if data is None: return
            data.x = left_x
            data.y = left_y
            
            # 如果坐标被限制，更新输入框显示的值
            centered_x, centered_y = tools.convert_to_centered(left_x, left_y)
            if centered_x != x or centered_y != y:
                self.pos_x_entry_var.set(str(centered_x))
                self.pos_y_entry_var.set(str(centered_y))
            
            # 触发卡片值变更事件，通知其他组件更新显示
            event.dispatch_event(const.event_card_value_changed, data.uid)
        except ValueError:
            # 输入的不是有效的数字，恢复为当前位置的值
            if self.data:
                x, y = tools.convert_to_centered(self.data.x, self.data.y)
                self.pos_x_entry_var.set(str(x))
                self.pos_y_entry_var.set(str(y))

    def on_face_change(self, *args):
        """复选框状态变化事件处理函数
        
        处理初始正面复选框的状态变化，更新卡片的firstopen属性
        
        Args:
            *args: 可变参数列表
        """
        data = self.data
        if data is None: return tools.toast("请先选择一张牌")
        
        # 更新卡片的firstopen属性
        data.firstopen = self.chb_var.get()